#include "xsmallrequest.h"
#include "xtransaction.h"
#include "xtransport.h"
#include "xrequest.h"
#include "libxsip.h"
#include "msg.h"
#include "xsip.h"
#include <assert.h>

#define MAX(a, b) ((a > b) ? a : b)
#define MIN(a, b) ((a < b) ? a : b)

static DelayInterval convert2DelayInterval(int ts)
{
	time_base_seconds sec = ts / 1000;
	time_base_seconds usec = (ts % 1000) * 1000;
	return DelayInterval(sec, usec);
}

XSIPSmallClientTransactionState::XSIPSmallClientTransactionState(void *transaction)
{
	m_transaction = (XSIPTransaction*)transaction;
	m_ts = 250;
	if (m_transaction->getTransport()->getObject("XSIPDgramTransport"))
		m_retran = new RetranEntry(this, convert2DelayInterval(m_ts));
	else
		m_retran = NULL;
	m_ts = MIN(m_ts * 2, 2000);
	m_timeout = new TimeoutEntry(this, DelayInterval(15, 0));
	m_contentIndex = -1;
	m_ret = XSIP_EVENT_CODE_SUCCESS;
}

XSIPSmallClientTransactionState::~XSIPSmallClientTransactionState()
{
	if (m_retran)
		delete m_retran;
	delete m_timeout;
}

int XSIPSmallClientTransactionState::touch()
{
	int ret = 0;
	if (m_state == TSX_STATE_SEND_REQUEST)
	{
		XSIPRequest *request = m_transaction->getRequest();
		XSIPTransport *transport = m_transaction->getTransport();
		ret = transport->sendMessage(request, m_transaction);
		if (ret == 0)
		{
			if (m_retran)
				XSIPEngine::getCurrentThreadEngine()->addEntry(m_retran);
			XSIPEngine::getCurrentThreadEngine()->addEntry(m_timeout);
		}
		else 
		{
			ret = XSIP_EVENT_CODE_FAILED;
			complete();
		}
	}
	else if (m_state == TSX_STATE_SEND_REQUEST_AGAIN)
	{
		XSIPRequest *request = m_transaction->getRequest();
		XSIPTransport *transport = m_transaction->getTransport();
		ret = transport->sendMessage(request, m_transaction);
		if (m_retran)
			XSIPEngine::getCurrentThreadEngine()->updateEntry(m_retran, convert2DelayInterval(m_ts));
		m_ts = MIN(m_ts * 2, 2000);
	}
	else if (m_state == TSX_STATE_RECV_RESPONSE)
	{
		XSIPCall* call = (XSIPCall*) m_transaction->getOwner()->getObject("XSIPCall");
		XSIPResponse* response = m_transaction->getResponse();
		XSIPDataReceiver* receiver = call->getDataReceiver();
		if (receiver)
		{
			receiver->onResponse(m_transaction, response);
			receiver->onDataRead(m_transaction, response->getContent()->getData(), response->getContent()->getSize());
			receiver->onComplete(m_transaction, 0, response);
		}
		m_ret = XSIP_EVENT_CODE_SUCCESS;
		complete();
	}
	else if (m_state == TSX_STATE_RECV_N_RESPONSE)
	{
		XSIPCall* call = (XSIPCall*) m_transaction->getOwner()->getObject("XSIPCall");
		XSIPResponse* response = m_transaction->getResponse();
		XSIPRequest *request = m_transaction->getRequest();
		XSIPPayloadOffsetHeader payload(response->getHeader()->getHeader(XSIP_MSG_HEADER_PAYLOAD_OFFSET));
		
		if (m_contentIndex < payload.getContentIndex())
		{
			m_contentIndex = payload.getContentIndex();

			XSIPDataReceiver* receiver = call->getDataReceiver();
			if ((m_contentIndex+1) == response->getResponseCount())
			{
				if (receiver)
				{
					receiver->onDataRead(m_transaction, response->subResponse(m_contentIndex)->getContent()->getData(), response->subResponse(payload.getContentIndex())->getContent()->getSize());
					receiver->onComplete(m_transaction, 0, response);
				}
				m_ret = XSIP_EVENT_CODE_SUCCESS;
				complete();
			}
			else
			{
				if (receiver)
				{
					if (payload.getContentIndex() == 0)
					{
						response->getHeader()->addHeader(XSIP_MSG_HEADER_CONTENT_LENGTH, response->getContent()->getSize());
						receiver->onResponse(m_transaction, response);
					}
					receiver->onDataRead(m_transaction, response->subResponse(m_contentIndex)->getContent()->getData(), response->subResponse(payload.getContentIndex())->getContent()->getSize());
				}
				request->getHeader()->addHeader(XSIP_MSG_HEADER_PAYLOAD_OFFSET_ACK, m_contentIndex);
				XSIPTransport *transport = m_transaction->getTransport();
				ret = transport->sendMessage(request, m_transaction);
				if (ret != 0)
				{
					m_ret = XSIP_EVENT_CODE_FAILED;
					complete();
				}
			}
		}
	}
	else if (m_state == TSX_STATE_TIMEOUT)
	{
		if (m_retran)
			XSIPEngine::getCurrentThreadEngine()->removeEntry(m_retran);
		m_ret = XSIP_EVENT_CODE_TIMEOUT;
		m_transaction->onEvent(m_transaction, m_ret);
	}
	return ret;
}

void XSIPSmallClientTransactionState::timeout()
{
	setState(TSX_STATE_TIMEOUT);
	touch();
}

void XSIPSmallClientTransactionState::complete()
{
	if (m_retran)
		XSIPEngine::getCurrentThreadEngine()->removeEntry(m_retran);
	XSIPEngine::getCurrentThreadEngine()->removeEntry(m_timeout);
	m_transaction->onEvent(m_transaction, m_ret);
}

XSIPSmallServerTransactionState::XSIPSmallServerTransactionState()
{
	m_ts = 250;
	m_retran = NULL;//new RetranEntry(this, convert2DelayInterval(m_ts));
	//m_ts = MIN(m_ts * 2, 2000);
	m_timeout = new TimeoutEntry(this, DelayInterval(15, 0));
	m_ack = -1;
	m_ret = XSIP_EVENT_CODE_TIMEOUT;
}

XSIPSmallServerTransactionState::~XSIPSmallServerTransactionState()
{
	delete m_timeout;
}

int XSIPSmallServerTransactionState::touch()
{
	int ret = 0;
	if (m_state == TSX_STATE_RECV_REQUEST)
	{
		XSIPRequest *request = m_transaction->getRequest();
		XSIPEngine::getCurrentThreadEngine()->onCommand((XSIPCommand*)m_transaction->getObject("XSIPCommand"), m_transaction, request);
		XSIPEngine::getCurrentThreadEngine()->addEntry(m_timeout);
	}
	else if (m_state == TSX_STATE_SEND_RESPONSE)
	{
		XSIPTransport *transport = m_transaction->getTransport();
		XSIPResponse *response = m_transaction->getResponse();
		response->divide();
		if (response->getResponseCount() > 1)
		{
			setState(TSX_STATE_SEND_N_RESPONSE);
			ret = touch();
			if (ret != 0)
			{
				m_ret = XSIP_EVENT_CODE_FAILED;
				complete();
			}
			else
				m_ret = XSIP_EVENT_CODE_SUCCESS;
		}
		else
		{
			ret = transport->sendMessage(response, m_transaction);
			if (ret != 0)
			{
				m_ret = XSIP_EVENT_CODE_FAILED;
				complete();
			}
			else
				m_ret = XSIP_EVENT_CODE_SUCCESS;
		}
	}
	else if (m_state == TSX_STATE_SEND_N_RESPONSE)
	{
		XSIPTransport *transport = m_transaction->getTransport();
		XSIPResponse *response = m_transaction->getResponse();
		
		XSIPResponse *sub = response->subResponse(0);
		XSIPContent* content = sub->getContent();
		XSIPHeader *header = sub->getHeader();
		header->addHeader(XSIP_MSG_HEADER_PAYLOAD_OFFSET, XSIPPayloadOffsetHeader::getPayloadOffset(0, content->getSize(), response->getContent()->getSize()));
		ret = transport->sendMessage(sub, m_transaction);
	}
	else if (m_state == TSX_STATE_RECV_1_REQUEST)
	{
		XSIPTransport *transport = m_transaction->getTransport();
		XSIPResponse *response = m_transaction->getResponse();
		XSIPRequest *request = m_transaction->getRequest();
		XSIPPayloadOffsetAckHeader ack(request->getHeader()->getHeader(XSIP_MSG_HEADER_PAYLOAD_OFFSET_ACK));
		if (m_ack <= ack.getAck())
		{
			m_ack = ack.getAck() + 1;
			XSIPResponse *sub = response->subResponse(m_ack);
			XSIPContent* content = sub->getContent();
			XSIPHeader *header = sub->getHeader();
			header->addHeader(XSIP_MSG_HEADER_PAYLOAD_OFFSET, XSIPPayloadOffsetHeader::getPayloadOffset(m_ack, content->getSize(), response->getContent()->getSize()));
			ret = transport->sendMessage(sub, m_transaction);
			if (ret != 0)
			{
				m_ret = XSIP_EVENT_CODE_FAILED;
				complete();
			}
		}
	}
	else if (m_state == TSX_STATE_TIMEOUT)
	{
		complete();
	}
	return ret;
}

void XSIPSmallServerTransactionState::timeout()
{
	setState(TSX_STATE_TIMEOUT);
	touch();
}

void XSIPSmallServerTransactionState::complete()
{
	m_transaction->onEvent(m_transaction, m_ret);
}

RetranEntry::RetranEntry(XSIPTransactionState* state, DelayInterval to)
	: DelayQueueEntry(to), m_state(state)
{

}

RetranEntry::~RetranEntry()
{

}

void RetranEntry::handleTimeout()
{
	m_state->setState(TSX_STATE_SEND_REQUEST_AGAIN);
	m_state->touch();
}

TimeoutEntry::TimeoutEntry(XSIPTransactionState* state, DelayInterval to)
	: DelayQueueEntry(to), m_state(state)
{

}

TimeoutEntry::~TimeoutEntry()
{

}

void TimeoutEntry::handleTimeout()
{
	m_state->timeout();
}